package com.sleepfish.activiti.framework.core;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.annotations.VisibleForTesting;
import com.sleepfish.activiti.domain.BpmTaskAssignRule;
import com.sleepfish.activiti.enums.BpmTaskAssignRuleTypeEnum;
import com.sleepfish.activiti.framework.core.script.BpmTaskAssignScript;
import com.sleepfish.activiti.service.IBpmTaskAssignRuleService;
import com.sleepfish.activiti.util.CommonStatusEnum;
import com.sleepfish.activiti.util.ServiceExceptionUtil;
import com.sleepfish.common.activiti.CollectionUtils;
import com.sleepfish.common.activiti.ErrorCodeConstants;
import com.sleepfish.common.core.domain.entity.SysDept;
import com.sleepfish.common.core.domain.entity.SysRole;
import com.sleepfish.common.core.domain.entity.SysUser;
import com.sleepfish.common.utils.json.JsonUtils;
import com.sleepfish.framework.web.service.SysPermissionService;
import com.sleepfish.system.domain.SysPost;
import com.sleepfish.system.service.ISysDeptService;
import com.sleepfish.system.service.ISysPostService;
import com.sleepfish.system.service.ISysUserService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.el.ExpressionManager;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntityManager;

import java.util.*;


/**
 * 自定义的流程任务的 assignee 负责人的分配
 * 第一步，获得对应的分配规则；
 * 第二步，根据分配规则，计算出分配任务的候选人。如果找不到，则直接报业务异常，不继续执行后续的流程；
 * 第三步，随机选择一个候选人，则选择作为 assignee 负责人。
 *
 * @author xiaochao
 */
@Slf4j
public class BpmUserTaskActivitiBehavior extends UserTaskActivityBehavior {

    @Setter
    private IBpmTaskAssignRuleService bpmTaskRuleService;



    @Setter
    private ISysDeptService deptApi;
    @Setter
    private ISysPostService postApi;
    @Setter
    private ISysUserService adminUserApi;
    @Setter
    private SysPermissionService permissionApi;

    /**
     * 任务分配脚本
     */
    private Map<Long, BpmTaskAssignScript> scriptMap = Collections.emptyMap();

    public BpmUserTaskActivitiBehavior(UserTask userTask) {
        super(userTask);
    }

    public void setScripts(List<BpmTaskAssignScript> scripts) {
        this.scriptMap = CollectionUtils.convertMap(scripts, script -> script.getEnum().getId());
    }

    @Override
    protected void handleAssignments(TaskEntityManager taskEntityManager,
                                     String assignee, String owner, List<String> candidateUsers, List<String> candidateGroups,
                                     TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution) {
        // 第一步，获得任务的规则
        BpmTaskAssignRule rule = getTaskRule(task);
        // 第二步，获得任务的候选用户们
        Set<Long> candidateUserIds = calculateTaskCandidateUsers(task, rule);
        // 第三步，设置一个作为负责人
        Long assigneeUserId = chooseTaskAssignee(candidateUserIds);
        taskEntityManager.changeTaskAssignee(task, String.valueOf(assigneeUserId));
    }

    private BpmTaskAssignRule getTaskRule(TaskEntity task) {
        List<BpmTaskAssignRule> taskRules = bpmTaskRuleService.getTaskAssignRuleListByProcessDefinitionId(task.getProcessDefinitionId(),
                task.getTaskDefinitionKey());
        if (CollUtil.isEmpty(taskRules)) {
            throw new ActivitiException(StrUtil.format("流程任务({}/{}/{}) 找不到符合的任务规则",
                    task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey()));
        }
        if (taskRules.size() > 1) {
            throw new ActivitiException(StrUtil.format("流程任务({}/{}/{}) 找到过多任务规则({})",
                    task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), taskRules.size()));
        }
        return taskRules.get(0);
    }

    private Long chooseTaskAssignee(Set<Long> candidateUserIds) {
        // TODO 芋艿：未来可以优化下，改成轮询的策略
        int index = RandomUtil.randomInt(candidateUserIds.size());
        return CollUtil.get(candidateUserIds, index);
    }

    @VisibleForTesting
    Set<Long> calculateTaskCandidateUsers(TaskEntity task, BpmTaskAssignRule rule) {
        Set<Long> assigneeUserIds = null;
        System.out.println("11111111111111111111");
        System.out.println(BpmTaskAssignRuleTypeEnum.ROLE.getType());
        System.out.println(rule.getType());
        if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) {
            assigneeUserIds = calculateTaskCandidateUsersByRole(task, rule);
        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) {
            assigneeUserIds = calculateTaskCandidateUsersByDeptMember(task, rule);
        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) {
            assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(task, rule);
        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.POST.getType(), rule.getType())) {
            assigneeUserIds = calculateTaskCandidateUsersByPost(task, rule);
        } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) {
            assigneeUserIds = calculateTaskCandidateUsersByUser(task, rule);
        }  else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) {
            assigneeUserIds = calculateTaskCandidateUsersByScript(task, rule);
        }

        // 移除被禁用的用户
        removeDisableUsers(assigneeUserIds);
        // 如果候选人为空，抛出异常 TODO 芋艿：没候选人的策略选择。1 - 挂起；2 - 直接结束；3 - 强制一个兜底人
        if (CollUtil.isEmpty(assigneeUserIds)) {
            log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]",
                    task.getId(), task.getProcessDefinitionId(), task.getTaskDefinitionKey(), JsonUtils.toJsonString(rule));
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER);
        }
        System.out.println(assigneeUserIds.toString());

        return assigneeUserIds;
    }

    private Set<Long> calculateTaskCandidateUsersByRole(TaskEntity task, BpmTaskAssignRule rule) {
        Set<Long> options = rule.getOptions();
        Long[] roleIds =new Long[options.size()];
        options.toArray(roleIds);
        Set<Long> sets=adminUserApi.getUserRoleIdListByRoleIds(roleIds);
        System.out.println("我是角色");
        System.out.println(sets.toString());
        return sets;
    }

    private Set<Long> calculateTaskCandidateUsersByDeptMember(TaskEntity task, BpmTaskAssignRule rule) {
        List<SysUser> users =new ArrayList<>();
        Set<Long> options = rule.getOptions();
        for (Long option : options) {
            SysUser sysUser =new SysUser();
            sysUser.setDeptId(option);
            List<SysUser> sysUsers = adminUserApi.selectUserList(sysUser);
            users.addAll(sysUsers);
        }
        return CollectionUtils.convertSet(users, SysUser::getUserId);
    }

    private Set<Long> calculateTaskCandidateUsersByDeptLeader(TaskEntity task, BpmTaskAssignRule rule) {
        List<SysDept> depts =new ArrayList<>();
        Set<Long> options = rule.getOptions();
        for (Long option : options) {
            SysDept sysDept = deptApi.selectDeptById(option);
            SysUser sysUser = adminUserApi.selectUserByUserName(sysDept.getLeader());
            sysDept.setLeaderId(sysUser.getUserId());
            depts.add(sysDept);
        }
        return CollectionUtils.convertSet(depts, SysDept::getLeaderId);
    }

    private Set<Long> calculateTaskCandidateUsersByPost(TaskEntity task, BpmTaskAssignRule rule) {
        Set<Long> options = rule.getOptions();
        Long [] ids = new Long[options.size()];
        options.toArray(ids);

        List<SysUser> users = adminUserApi.getUsersByPostIds(ids);
        return CollectionUtils.convertSet(users, SysUser::getUserId);
    }

    private Set<Long> calculateTaskCandidateUsersByUser(TaskEntity task, BpmTaskAssignRule rule) {
        return rule.getOptions();
    }

//    private Set<Long> calculateTaskCandidateUsersByUserGroup(TaskEntity task, BpmTaskAssignRule rule) {
//        List<BpmUserGroupDO> userGroups = userGroupService.getUserGroupList(rule.getOptions());
//        Set<Long> userIds = new HashSet<>();
//        userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds()));
//        return userIds;
//    }

    private Set<Long> calculateTaskCandidateUsersByScript(TaskEntity task, BpmTaskAssignRule rule) {
        // 获得对应的脚本
        List<BpmTaskAssignScript> scripts = new ArrayList<>(rule.getOptions().size());
        rule.getOptions().forEach(id -> {
            BpmTaskAssignScript script = scriptMap.get(id);
            if (script == null) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.TASK_ASSIGN_SCRIPT_NOT_EXISTS, id);
            }
            scripts.add(script);
        });
        // 逐个计算任务
        Set<Long> userIds = new HashSet<>();
        scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(task)));
        return userIds;
    }

    @VisibleForTesting
    void removeDisableUsers(Set<Long> assigneeUserIds) {
        if (CollUtil.isEmpty(assigneeUserIds)) {
            return;
        }
        Map<Long, SysUser> userMap =new HashMap<>();
        for (Long id : assigneeUserIds) {
            SysUser sysUser = adminUserApi.selectUserById(id);
            userMap.put(sysUser.getUserId(),sysUser);
        }

        assigneeUserIds.removeIf(id -> {
            SysUser user = userMap.get(id);
            System.out.println(user.getStatus());
            System.out.println(CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus()));
            return user == null || !"0".equals(user.getStatus());
        });
    }

}
